Skip to content

feat: single @guardrail decorator with validator strategy pattern [AL-288]#736

Open
apetraru-uipath wants to merge 1 commit intomainfrom
feat/guardrails_decorators_second
Open

feat: single @guardrail decorator with validator strategy pattern [AL-288]#736
apetraru-uipath wants to merge 1 commit intomainfrom
feat/guardrails_decorators_second

Conversation

@apetraru-uipath
Copy link
Copy Markdown
Contributor

@apetraru-uipath apetraru-uipath commented Mar 27, 2026

What changed?

Replace three type-specific guardrail decorators (@pii_detection_guardrail, @prompt_injection_guardrail, @deterministic_guardrail) with a single unified @guardrail(validator=..., action=..., name=..., stage=...) decorator using the strategy pattern.

New API:

@guardrail(validator=PromptInjectionValidator(threshold=0.5), action=BlockAction(), name="LLM Prompt Injection", stage=GuardrailExecutionStage.PRE)
@guardrail(validator=PIIValidator(entities=[PIIDetectionEntity(PIIDetectionEntityType.EMAIL, 0.5)]), action=LogAction(), name="LLM PII")
def create_llm():
    return UiPathChat(model="gpt-4o")

Key changes:

  • Add decorators/validators/ package with GuardrailValidatorBase, PIIValidator, PromptInjectionValidator, DeterministicValidator
  • Validators carry what to check (entities, threshold, rules); decorator carries how to respond (action, name, stage)
  • A single validator instance can be reused across multiple @guardrail decorators with different actions or stages
  • Scope/stage validation at decoration time via validate_scope() / validate_stage()
  • Remove old type-specific decorator files (pii_detection.py, prompt_injection.py, deterministic.py) — 100% parity replacement, no backward compat shims
  • Fix double POST guardrail application on sync StructuredTool: StructuredTool.ainvoke (no coroutine) delegates to self.invoke via run_in_executor; overriding ainvoke in _GuardedTool caused POST to fire twice (words++++words++)
  • Run ruff check --fix + ruff format

Files:

  • src/uipath_langchain/guardrails/decorators/guardrail.py — new unified decorator
  • src/uipath_langchain/guardrails/decorators/validators/ — 4 new files
  • src/uipath_langchain/guardrails/decorators/{pii_detection,prompt_injection,deterministic}.py — deleted
  • src/uipath_langchain/guardrails/{__init__,decorators/__init__}.py — updated exports
  • samples/joke-agent-decorator/graph.py — rewritten to use new API

How has this been tested?

  • End-to-end run with uv run uipath run agent '{"topic": "money"}' — verified all 3 scopes (AGENT, LLM, TOOL) fire correctly, words++ filter applies exactly once
  • End-to-end run with uv run uipath run agent '{"topic": "joke about Andrei Petraru"}' — verified Agent PII (PERSON) blocks with AgentRuntimeError
  • uv run pytest — all tests pass (2 pre-existing failures for missing botocore/google optional deps, unrelated)
  • uv run ruff check src/uipath_langchain/guardrails/ — clean
  • uv run ruff format — 4 files reformatted

Are there any breaking changes?

  • Under Feature Flag
  • None
  • DB migrations required
  • API/interface removals or renames
  • Other

Old decorator names (pii_detection_guardrail, prompt_injection_guardrail, deterministic_guardrail) are removed. This branch replaces them entirely — callers must migrate to @guardrail(validator=...). No other public API is affected.

Ticket

AL-288

@apetraru-uipath apetraru-uipath force-pushed the feat/guardrails_decorators_second branch 3 times, most recently from 3747eaa to 04aa96b Compare March 27, 2026 15:41
supported_stages: ClassVar[list[GuardrailExecutionStage]] = []
"""Stages this validator supports. Empty list means all stages are allowed."""

def build_built_in_guardrail(
Copy link
Copy Markdown
Collaborator

@radu-mocanu radu-mocanu Mar 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit

Suggested change
def build_built_in_guardrail(
def built_in_guardrail(

or

Suggested change
def build_built_in_guardrail(
def get_built_in_guardrail(

Copy link
Copy Markdown
Collaborator

@radu-mocanu radu-mocanu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code looks clean.

However, I think we should consider moving the implementation to uipath-platform, since uipath guardrails are a platform concern, not necessarily a langchain one

@apetraru-uipath apetraru-uipath force-pushed the feat/guardrails_decorators_second branch 2 times, most recently from 332f7ff to c73bfc2 Compare March 31, 2026 12:14
Refactor guardrail decorator layer to use adapter/registry pattern:
- Move validator logic (PIIValidator, PromptInjectionValidator, CustomValidator),
  actions (LogAction, BlockAction), and @guardrail decorator core into
  uipath-platform; thin re-exports remain in uipath-langchain for backward compat
- Add _langchain_adapter.py with LangChainGuardrailAdapter implementing the
  GuardrailTargetAdapter Protocol; wraps BaseTool, BaseChatModel, StateGraph,
  CompiledStateGraph; auto-registered on import of uipath_langchain.guardrails
- BlockAction now raises GuardrailBlockException (platform); adapter converts to
  AgentRuntimeError at wrapper boundaries
- Update exception guards in middlewares/pii_detection.py and prompt_injection.py
  to re-raise GuardrailBlockException alongside AgentRuntimeError
- Delete decorators/_base.py, decorators/guardrail.py, decorators/validators/*
  (logic lives in platform); update thin re-exports in decorators/__init__.py

Made-with: Cursor

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@apetraru-uipath apetraru-uipath force-pushed the feat/guardrails_decorators_second branch from c73bfc2 to f99616d Compare April 1, 2026 19:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants